#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "cpuusage.h"

#include "DevicesTime.h"
#include "DevicesFlash.h"
#include "DevicesSPIFlash.h"

#include "stm32_eval_spi_sd.h"
#include "DriverFile.h"
#include "DriverLcd.h"

#include "NesCPU6502.h"
#include "NesPPU.h"
#include "NesMapper.h"
#include "NesSystem.h"


//FIL g_fileNESHandle = {0};
uint8_t g_ucNesBuff[512] = {0};

//extern uint8_t nesRom[];
NesInfoType g_typeNesInfo = {0};


int64_t lNesTimeGet(void)
{
    return lTimebaseGet();
}

void vNesDelayMs(int32_t iTime)
{
    if(iTime > 0)
        vTaskDelay(iTime / portTICK_RATE_MS);
}

void vNesMirrorTypeSet(int8_t cMirrorType)
{
    g_typeNesInfo.mirrorType = cMirrorType;
}

int8_t cNesFlashLoad(char *pucFileName)
{
    uint32_t uiAddr = 0;
    int32_t iLength, iLengthTotal, iLengthTemp;
    char cStrings[32] = {0};

    if(pucFileName == NULL)
        return 1;

    for(iLengthTotal = iLength = iFileSizeGet(pucFileName); iLength > 0; iLength -= sizeof(g_ucNesBuff))
    {
        iLengthTemp = iLength > sizeof(g_ucNesBuff) ? sizeof(g_ucNesBuff) : iLength;

        cFileRead(pucFileName, uiAddr, g_ucNesBuff, iLengthTemp);

        cSPIFlashWriteDatas(SPI_FLASH_NES_ROM_ADDR + uiAddr, g_ucNesBuff, iLengthTemp);

        sprintf(cStrings, "load: %.2f%%\r\n", (float)(iLengthTotal - iLength) / iLengthTotal * 100.0f);
        cLcdStringShow(32, 32, RED, BLACK, cStrings);

        uiAddr += sizeof(g_ucNesBuff);
    }

    return 0;
}

int8_t cNesLoad(void)
{
    if(g_typeNesInfo.NES_ROM == 0)
        return -1;

    /* 从封装好的Flash读取函数读取数据，会暂时关闭mcu icache，以真实的读取到文件头数据 */
    cSPIFlashReadDatas(g_typeNesInfo.NES_ROM, g_typeNesInfo.headers, 16);

    /* NES头部信息解析 */
    g_typeNesInfo.PRG_ROMSize    =  g_typeNesInfo.headers[4] * 1024 * 16;
    g_typeNesInfo.CHR_ROMSize    =  g_typeNesInfo.headers[5] * 1024 * 8;
    g_typeNesInfo.PRG_RAMSize    =  g_typeNesInfo.headers[8] * 1024 * 8;

    g_typeNesInfo.mapperType     =  g_typeNesInfo.headers[6] >> 4;
    g_typeNesInfo.mapperType    |=  g_typeNesInfo.headers[7] & 0xF0;

    g_typeNesInfo.mirrorType     =  g_typeNesInfo.headers[6] & 0x01;
    g_typeNesInfo.backedRAMExist = (g_typeNesInfo.headers[6] & 0x02) ? 1 : 0;
    g_typeNesInfo.trainerExist   = (g_typeNesInfo.headers[6] & 0x04) ? 1 : 0;
    g_typeNesInfo.VRAMExist      = (g_typeNesInfo.headers[6] & 0x08) ? 1 : 0;

    g_typeNesInfo.VSSystemExist  =  g_typeNesInfo.headers[7] & 0x01;
    g_typeNesInfo.TVSystemMode   =  g_typeNesInfo.headers[9] & 0x01;
    g_typeNesInfo.TVSystemModeSec = ((g_typeNesInfo.TVSystemMode == 0) ? (1000000 / 50) : (1000000 / 50));

    printf("NES information:\r\n");
    printf("PRG ROM size: %dByte, PRG RAM size: %dByte, CHR ROM size: %dByte\r\n",\
        g_typeNesInfo.PRG_ROMSize,\
        g_typeNesInfo.PRG_RAMSize,\
        g_typeNesInfo.CHR_ROMSize);
    printf("mapper: %d, mirrorType: %d, backedRAMExist: %d, trainerExist: %d, VRAMExist: %d\r\n",\
        g_typeNesInfo.mapperType,\
        g_typeNesInfo.mirrorType,\
        g_typeNesInfo.backedRAMExist,\
        g_typeNesInfo.trainerExist,\
        g_typeNesInfo.VRAMExist);


    /* 设置mapper结构体 */
    vNesMapperTypeSet(g_typeNesInfo.mapperType);


    /* 代码区域起始位置指针 */
    g_typeNesInfo.PRG_ROM = g_typeNesInfo.NES_ROM + 16;
    /* 图案区域起始位置指针 */
    g_typeNesInfo.CHR_ROM = g_typeNesInfo.NES_ROM + 16 + g_typeNesInfo.PRG_ROMSize;


    /* 加载程序代码到CPU */
    vNesCPUMemoryLoad();

    /* 加载图案信息到PPU */
    vNesPPUMemoryLoad();

    return 0;
}

int8_t cNesStart(void)
{
    vNesPPUStart();
    vNesCPUStart();

    /* 记录当前系统时间 */
    g_typeNesInfo.timeStart = lNesTimeGet();

    g_typeNesInfo.runing = 1;

    return 0;
}

int8_t cNesStop(void)
{
    g_typeNesInfo.runing = 0;

    return 0;
}

int8_t cNesReset(void)
{
    cNesStop();

    return 0;
}


char g_cNesRomPath[64] = "./NES/ROM/[217]  角色类 - 魂斗罗2 中文.nes";

void vNesInit(void)
{
    memset(&g_typeNesInfo, 0, sizeof(g_typeNesInfo));

    g_typeNesInfo.ptypeCPUInfo = ptyeNesCPUInfoGet();
    g_typeNesInfo.ptypePPUInfo = ptyeNesPPUInfoGet();
    g_typeNesInfo.ptypeNesInputInfo = ptyeNesInputInfoGet();
    g_typeNesInfo.NES_ROM = SPI_FLASH_NES_ROM_ADDR;


    vNesPPUInit();
    vNesCPUInit();
    vNesInputInit();


    printf("cNesFlashLoad: %s\r\n", g_cNesRomPath);
    cNesFlashLoad(g_cNesRomPath);

    /* 加载固件到模拟器 */
    cNesLoad();


    cNesStart();
}

void vNesExec(void)
{
    vNesCPUExec();

    vNesPPUExec();
}

NesInfoType *ptyeNesInfoGet(void)
{
    return &g_typeNesInfo;
}
